Skip to content

Support for PV systems with multiple arrays #1076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 249 commits into from
Jan 7, 2021

Conversation

wfvining
Copy link
Contributor

@wfvining wfvining commented Oct 6, 2020

Add a pvsystem.Array class and update API to support a single PVSystem instance with multiple arrays. Incorporates the multi-array API in to modelchain.ModelChain. Changes aim to be transparent to users who are modeling single-array systems, while the types of some fields (POA, DC power, etc) will change to a tuple for those who use the a PVSystem with multiple arrays.

Provides the three basic methods that depend on tilt and
azimuth and provides a container for other parameters
which may vary from array to array but do not vary within
a single array (racking, temperature model params,
albedo, strings, modules per string, etc.).
Removes array-related fields and adds _array field. All internal references
to array-related attributes now refer to the the corresponding attributes of
the Array class.

It is likely some of the Array attributes will need to be exposed on the
PVSystem instances via @properties; however, any of these instances are not
covered by tests so it will be necessary to carefully work through the other
places where PVsystem is used (ModelChain and child classes) to see what
needs to be done.
@cwhanse
Copy link
Member

cwhanse commented Oct 8, 2020

The pvsystem.py test failures raise an issue: how much of the current API do we want to maintain?

E.g., should PVSystem.celltemp_sapm still work? If so, should it iterate over PVSystem._arrays storing the output on each Array?

@cwhanse
Copy link
Member

cwhanse commented Oct 8, 2020

And the second, closely-related issue: where to put the output of a PVSystem or Array method? This seems to be a major design decision.

Currently, PVSystem.get_aoi returns the calculated values, and does not store the results on PVSystem. Propagating that behavior to PVSystem.Array.get_aoi pushes to the user the burden of managing aoi output for each Array to pass to the next step in the calculation.

It seems intuitive to store the results on Array but that may not be what a user expects.

@wfvining
Copy link
Contributor Author

wfvining commented Oct 9, 2020

I originally liked the idea of storing values like aoi in the Array class, but now I'm not sure. It puts a lot of responsibility in Array that is really managed externally by the callers. I agree we shouldn't put the burden of managing the output of each array on the caller, but I think I would want to store output of each array in the PVSystem class (or in a new class for multi-array PV systems).

I think if we can decide what to do with methods like PVSystem.sapm_effective_irradiance or PVSystem.sapm_celltemp that use quantities specific to an Array it will help make this decision.

@wholmgren
Copy link
Member

I'd rather not store any results on the objects. A few concerns:

It doubles the scope of the API. I feel like maintainer burden scales faster than linear with API.

Storing results on objects introduces a lot of state, which can make code a lot harder to reason about. ModelChain minimizes this problem with methods that largely compute consistent states. That wouldn't exist here without a lot of work.

It's not clear to me how it would actually simplify the PVSystem/Array internal interfaces.

@cwhanse
Copy link
Member

cwhanse commented Oct 9, 2020

Pushing on that thought - if no methods store results on Array or PVSystem, then results are stored on ModelChain? ModelChain attributes e.g.. aoi, total_irrad will need to be iterable and we'll need keys or ordering for Arrays to keep data aligned right.

@wholmgren
Copy link
Member

Yes, I thought we had already agreed that the ModelChain attributes would need to be iterable, but apologies if I'm misremembering. Are you suggesting that we remove those attributes from ModelChain and put them on Array instead? I agree that could help keep things orderly and I'm open to considering that. In addition to the concerns above, it would achieve order by scattering the ModelChain results across objects. I don't know if that's worth it or not. Back to iterables... I think it's reasonable to document that ModelChain.aoi is the same order as PVSystem.arrays. We could also consider a refactor of the ModelChain attributes into a single results dict, dataclass, or xarray.Dataset.

@cwhanse
Copy link
Member

cwhanse commented Oct 9, 2020

ModelChain does a lot of self-inspection to deliver upstream model output to downstream models.

Maybe the least common denominator is to use lists and expect each ModelChain attribute (containing output) to be a list of the same length as ModelChain.system.arrays. That seems brittle and may result in a lot of repetitive code, as started to appear in #1068. I'm coming to the opinion that Array instances need to be named with Array.name usable as a key somehow.

It seems useful to group model output into a single ModelChain attribute, that would certainly simplify the job of locating upstream model output for downstream functions. I'm not familiar with dataclasses or xarray, I'll look into those.

Use the temperature model parameters from the Array instance
inside PVSystem. Also properly initialize the parameters.
@wfvining
Copy link
Contributor Author

wfvining commented Oct 9, 2020

I'm going to keep pushing forward with a minimal working example of an Array class. My plan for now is to expose the array parameters via @propertys on PVSystem. That should get this to a point where the PVSystem class appears unchanged and keeps working as it does now with ModelChain.

wfvining and others added 5 commits October 12, 2020 08:35
Mistake in invocation of Array.__init__() in PVSystem.__init__()
Uses `@property` to provide getters and setters for Array attributes
in order to mimic the original behavior of the PVSystem class.
@cwhanse
Copy link
Member

cwhanse commented Oct 12, 2020

@wfvining will PVSystem allow more than one Array in this PR?

Since the Array attributes are all exposed via @propertys on PVSystem, we
can access them directly, reducing the number of changes needed to add the
Array class.
@wfvining
Copy link
Contributor Author

@cwhanse I've written it for just one Array to keep the changes minimal. We can branch from here, or repurpose the PR for the multi-array case and do everything in this branch.

wfvining and others added 11 commits October 12, 2020 13:18
Adds the @singleton_as_scalar decorator to make a PVSystem with a single
Array appear unchanged from the original PVSystem implementation.
Adds a decorator (@list_or_scalar) used to specify which parameters
should be lists of the same length as PVSystem._arrays. The decorator
handles validation by transforming non-list values to singleton lists
then comparing to the length of the _arrays field on the PVSystem
instance passed as the first parameter.

Needs tests with multiple Arrays, but must wait until we have an API for
adding multiple Arrays to a PVSystem.
This function is used in the test suite. It may not be needed otherwise,
but to move forward without needing to rewrite the tests I'm adding it
back to the PVSystem class as a wrapper around Array._infer_cell_type()
wfvining and others added 11 commits January 4, 2021 15:19
Make clear that the same model is applied to each array in the
system.
This reverts commit 68617fa. The current sphinx configuration
can't handle this because ModelChainResult does not have
an explicit __init__ method. This results in an OSError during
the build when sphinx tries to make the source code pages.
Removes redundant look-up in self.__dict__
Forgot to pass module_parameters to PVSystem constructor.
Requires slight modification of sphinx/source/conf.py to address
getting line numbers for the ModelChainResult.__init__ method
which is not explicitly defined (and therefore does not have a line
number).

Made the T TypeVar private so it does not clutter the attribute list
for ModelChainResult.

Add docstring for the PerArray type.
@wfvining
Copy link
Contributor Author

wfvining commented Jan 5, 2021

@kanderso-nrel Thanks for your review, it shook out some important stuff. I think I've addressed everything you mentioned if you want to take another look.

Co-authored-by: Will Holmgren <[email protected]>
Copy link
Member

@kandersolar kandersolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @wfvining! I suggest opening an issue to flesh out the ModelChainResult page on RTD a bit more -- it already lists out the attributes, which is the important thing, but attribute descriptions would be nice too.

Some minor nits below, otherwise LGTM

print(aoi)
system_multiarray.get_iam(aoi)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor complaint -- this prints (array(0.99182856), array(0.99952705)); would be nice if it showed (0.99182856, 0.99952705) instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how to achieve that without muddying the example. print(np.array(1)) will unwrap the value, but if you put arrays in a tuple you get the same result as above. We would need to resort to mapping str over the tuple, then printing it.

I guess tuple.__str__ calls __repr__ on its entries, not __str__. Seems like odd behavior to me, but what can you do.

Suggested change
system_multiarray.get_iam(aoi)
iam = system_multiarray.get_iam(aoi)
print(tuple(map(str, iam)))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I guess I was thinking that, if there was an issue to fix, it was that system.get_iam returned tuple of array instead of tuple of float, not that the printing here needs to be fixed. Let's not worry about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth a separate issue to address the return types from the function layer.

@wholmgren
Copy link
Member

I plan to merge this tomorrow unless I hear objections.

@wholmgren
Copy link
Member

Thanks @wfvining for the huge contribution and everyone else that contributed to the discussion and reviews!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple arrays in a PV system
8 participants